package work.chief.backend.core.builder;


import org.apache.commons.lang3.StringUtils;
import work.chief.backend.core.builder.sql.MySQLDialect;
import work.chief.backend.core.builder.sql.SQLDialect;
import work.chief.backend.domain.Field;
import work.chief.backend.domain.enumeration.FieldTypeEnum;
import work.chief.backend.domain.enumeration.MockTypeEnum;
import work.chief.backend.domain.schame.TableSchema;

import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.stream.Collectors;

public class SqlBuilder {

    private SQLDialect sqlDialect = new MySQLDialect();

    /**
     * 构造建表sql
     */
    public String buildCreateTableSql(TableSchema tableSchema) {

//        构造模板
        String template = "%s\n" +
                "create table if not exists %s\n" +
                "(\n" +
                "%s\n" +
                ") %s;";

//        构造表名
        String tableName = sqlDialect.warpFieldName(tableSchema.getTableName());
        String dbName = tableSchema.getDbName();

//        构造表前缀注释
        String tableComment = tableSchema.getTableComment();
        if (StringUtils.isBlank(tableComment)) {
            tableComment = tableName;
        }
        String tablePrefixComment = String.format("-- %s", tableComment);

//        构造表后缀注释
        String tableSuffixComment = String.format("comment '%s'", tableComment);

//        构造表字段
        List<Field> fieldList = tableSchema.getFieldList();
        StringBuilder fieldStrBuilder = new StringBuilder();
        int fieldSize = fieldList.size();
        for (int i = 0; i < fieldSize; i++) {
            Field field = fieldList.get(i);
            fieldStrBuilder.append(buildCreateFieldSql(field));
            if (i != fieldSize - 1) {
                fieldStrBuilder.append(",\n");
            }
        }
        String fieldStr = fieldStrBuilder.toString();

        String result = String.format(template, tablePrefixComment, tableName, fieldStr, tableSuffixComment);

        return result;
    }

    public String buildCreateFieldSql(Field field) {
        if (field == null) {
            return "error";
        }
        String fieldName = sqlDialect.warpFieldName(field.getFieldName());
        String fieldType = field.getFieldType();
        String defaultValue = field.getDefaultValue();
        String fieldComment = field.getFieldComment();
        boolean fieldNotNull = field.isNotNull();
        boolean primaryKey = field.isPrimaryKey();
        boolean autoIncrement = field.isAutoIncrement();
        String mockType = field.getMockType();

        StringBuilder fieldStrBuilder = new StringBuilder();
//        字段名
        fieldStrBuilder.append(fieldName);
//        字段类型
        fieldStrBuilder.append(" ").append(fieldType);
//        是否非空
        fieldStrBuilder.append(" ").append(fieldNotNull ? "not null" : "null");
//        默认值
        if (StringUtils.isNotBlank(defaultValue)) {
            if ("规则".equals(mockType)) {
            } else {
                fieldStrBuilder.append(" ").append("default").append(getValueStr(field, defaultValue));
            }
        }
//        是否自增
        if (autoIncrement) {
            fieldStrBuilder.append(" ").append("auto_increment");
        }
//        注释
        if (StringUtils.isNotBlank(fieldComment)) {
            fieldStrBuilder.append(" ").append(String.format("comment '%s'", fieldComment));
        }
//        是否主键
        if (primaryKey) {
            fieldStrBuilder.append(" ").append("primary key");
        }
        return fieldStrBuilder.toString();

    }

    public String buildInsertSql(TableSchema tableSchema, List<Map<String, Object>> dataList) {
//        新建一条插入语句的模板
        String template = "insert into %s (%s) values (%s);";

        //包装表名
        String tableName = sqlDialect.wrapTableName(tableSchema.getTableName());
        String dbName = tableSchema.getDbName();
        if (StringUtils.isNotBlank(dbName)) {
            tableName = String.format("%s.%s", dbName, tableName);
        }
//        获取字段集
        List<Field> fieldList = tableSchema.getFieldList();

//        过滤不模拟的字段
        fieldList.stream().filter(field -> {
            MockTypeEnum mockTypeEnum = Optional.ofNullable(MockTypeEnum.getEnumByValue(field.getMockType())).orElse(MockTypeEnum.NONE);
            return !MockTypeEnum.NONE.equals(mockTypeEnum);
        }).collect(Collectors.toList());

        StringBuilder dataSql = new StringBuilder();
        int total = dataList.size();
        for (int i = 0; i < total; i++) {
            Map<String, Object> fieldRow = dataList.get(i);
            String keyStr = fieldList.stream()
                    .map(field -> sqlDialect.warpFieldName(field.getFieldName()))
                    .collect(Collectors.joining(", "));
            String valueStr = fieldList.stream()
                    .map(field -> getValueStr(field, fieldRow.get(field.getFieldName())))
                    .collect(Collectors.joining(", "));
//            填充模板
            String result = String.format(template, tableName, keyStr, valueStr);
            dataSql.append(result);
//            除最后一行外，都要加换行符
            if (i != total - 1) {
                dataSql.append("\n");
            }

        }
        return dataSql.toString();
    }


    public static String getValueStr(Field field, Object value) {
        if (field == null || value == null) {
            return "''";
        }
        FieldTypeEnum fieldTypeEnum = Optional.ofNullable(FieldTypeEnum.getEnumByValue(field.getFieldType()))
                .orElse(FieldTypeEnum.TEXT);
        String result = String.valueOf(value);
        switch (fieldTypeEnum) {
            case DATETIME:
            case TIMESTAMP:
                return result.equalsIgnoreCase("CURRENT_TIMESTAMP") ? result : String.format("'%s'", value);
            case DATE:
            case TIME:
            case CHAR:
            case VARCHAR:
            case TINYTEXT:
            case TEXT:
            case MEDIUMTEXT:
            case LONGTEXT:
            case TINYBLOB:
            case BLOB:
            case MEDIUMBLOB:
            case LONGBLOB:
            case BINARY:
            case VARBINARY:
                return String.format("'%s'", value);
            default:
                return result;
        }
    }
}
